package com.mnsq.common.service.impl;

import com.mnsq.common.bean.condition.DocumentCondition;
import com.mnsq.common.bean.dto.PageInfo;
import com.mnsq.common.service.MongoService;
import com.mongodb.BasicDBObject;
import com.mongodb.DBObject;
import com.mongodb.QueryBuilder;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.BasicQuery;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.stereotype.Repository;

import java.util.List;
import java.util.Map;

/**
 * @author 周俭
 */
@Repository
public class MongoServiceImpl<T> implements MongoService<T> {
    @Autowired
    private MongoTemplate mongoTemplate;

    @Override
    public void save(T t) {
        mongoTemplate.save(t);
    }

    @Override
    public void saveAll(Iterable<T> v) {
        for (T t : v) {
            this.save(t);
        }
    }

    @Override
    public PageInfo page(DocumentCondition documentCondition) {
        Query query = packageQuery(documentCondition);
        Class<T> doc = documentCondition.getDoc();
        List documents = mongoTemplate.find(query, doc);
        long size = mongoTemplate.count(query, doc);
        PageInfo pg = new PageInfo();
        pg.setContent(documents);
        pg.setPageNum(documentCondition.getPageParams().getPageNum());
        pg.setTotalSize(size);
        return pg;
    }

    @Override
    public Object detail(DocumentCondition documentCondition) {
        Query query = this.packageQuery(documentCondition);
        return mongoTemplate.findOne(query, documentCondition.getDoc());
    }

    @Override
    public void update(DocumentCondition documentCondition) {
        Query query = this.packageQuery(documentCondition);
        Update update = new Update();
        Map<String, String> updateParams = documentCondition.getUpdateParams();
        for (Map.Entry<String, String> mp : updateParams.entrySet()) {
            update.set(mp.getKey(), mp.getValue());
        }
        mongoTemplate.updateFirst(query, update, documentCondition.getDoc());
    }

    private Query packageQuery(DocumentCondition documentCondition) {
        DBObject fields = new BasicDBObject();
        List<String> fds = documentCondition.getQueryColumns();
        if (CollectionUtils.isNotEmpty(fds)) {
            for (String s : fds) {
                fields.put(s, 1);
            }
        }

        QueryBuilder queryBuilder = new QueryBuilder();
        BasicDBObject queryFds = new BasicDBObject();
        if (MapUtils.isNotEmpty(documentCondition.getQueryParams())) {
            Map<String, String> queryParams = documentCondition.getQueryParams();
            for (Map.Entry<String, String> mp : queryParams.entrySet()) {
                queryFds.put(mp.getKey(), mp.getValue());
            }
        }
        queryBuilder.and(queryFds);

        Query query = new BasicQuery(queryBuilder.get().toString(), fields.toString());
        if (documentCondition.getPageParams() != null && documentCondition.getPageParams().getPageSize() > 0 && documentCondition.getPageParams().getPageNum() >= 0) {
            Pageable of = PageRequest.of(documentCondition.getPageParams().getPageNum(), documentCondition.getPageParams().getPageSize());
            query.with(of);
        }

        Sort sort = null;
        if (documentCondition.getPageParams() != null && MapUtils.isNotEmpty(documentCondition.getPageParams().getSortParams())) {
            for (Map.Entry<String, Integer> mp : documentCondition.getPageParams().getSortParams().entrySet()) {
                if (sort == null) {
                    if (mp.getValue() != null && mp.getValue() == 1) {
                        sort = Sort.by(mp.getKey()).ascending();
                    } else {
                        sort = Sort.by(mp.getKey()).descending();
                    }
                } else {
                    if (mp.getValue() != null && mp.getValue() == 1) {
                        sort.and(Sort.by(mp.getKey()).ascending());
                    } else {
                        sort.and(Sort.by(mp.getKey()).descending());
                    }
                }
            }
            query.with(sort);
        }

        return query;
    }
}
